using System;
using System.Collections.Generic;
using System.Text;
using AgilePlannerDT.Persister;
using System.ComponentModel;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;
using AgilePlannerDT.Utils;
using AgilePlannerDT.Persister.Impl.File;
using System.Windows.Media;
using AgilePlannerDT.Display.Events;
using System.Collections;

namespace AgilePlannerDT.Data.Impl
{
    public class ProjectDataObject : Project
    {
        #region Constants

        public static readonly string ID_PROPERTY = "ID";
        public static readonly string NAME_PROPERTY = "Name";

        #endregion

        #region Attributes

        protected Backlog backlog;

        protected Legend legend;

        protected List<Iteration> iterations = new List<Iteration>();

        protected List<TeamMember> teamMember = new List<TeamMember>();

        protected SynchronousPersister persister;

        protected long id, maxid = 0;

        protected string name;

        protected Dictionary<long, IndexCard> cards = new Dictionary<long, IndexCard>();

        protected FileSystemIDGenerator generator;

        #endregion

        #region Events

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Constructors

        public ProjectDataObject()
            : this("Project")
        {
        }

        public ProjectDataObject(Project original)
        {
         //   this();
            this.generator = FileSystemIDGenerator.GetInstance();
            this.generator.Init(((ProjectDataObject)original).getGenerator().GetNextID());
            this.id = original.ID;
            Backlog = (Backlog)original.Backlog.Clone();
            Backlog.Project = this;
            
            this.legend = (Legend)original.Legend.Clone();
            this.name = original.Name;

            foreach (Iteration iter in original.IterationChildren)
            {
                AddIteration((Iteration)iter.Clone());
            }
            foreach (TeamMember member in original.TeamMemberChildren)
            {
                AddTeamMember((TeamMember)member.Clone());
            }
           
        }

        public ProjectDataObject(SynchronousPersister persister)
            : this("Project", persister)
        {
        }

        public ProjectDataObject(string name, SynchronousPersister persister)
        {
            this.name = name;
            this.persister = persister;
        }

        public ProjectDataObject(string name)
        {
            //special for the Local Synchornize 
            this.generator = FileSystemIDGenerator.GetInstance();
            generator.Reset();
            this.name = name;
            this.id = 0;
            CreateBacklog(0, 0, 0, 0);
            this.backlog.Parent = this.id;
            this.legend = new LegendDataObject();
        }

        #endregion

        #region AbstractRoot Members

        public long ID
        {
            get { return this.id; }
            set
            {
                if (this.id == value)
                {
                    return;
                }
                this.id = value;
                //object before = Clone();
                
                //object after = Clone();
                //Notify(ID_PROPERTY, before, after);
                ////Notify(ID_PROPERTY);
            }
        }

        public string Name
        {
            get { return this.name; }
            set
            {
                if (this.name == value)
                {
                    return;
                }
                this.name = value;
                //object before = Clone();
               
                //object after = Clone();
                //Notify(NAME_PROPERTY, before, after);
                //Notify(NAME_PROPERTY);
            }
        }

   

        #endregion

        #region Project Members

        public void AddTeamMember(TeamMember member)
        {
            if (teamMember.Contains(member))
                throw new ForbiddenOperationException("Conflicting user:" + member.Name + " already in use!");
            else
            {
                this.teamMember.Add(member);
            }
        }

        public void AddIteration(Iteration iteration)
        {
            //check for duplicate keys
            if (cards.ContainsKey(iteration.ID))
                throw new ForbiddenOperationException("Conflicting keys: ID " + iteration.ID + " already in use!");
            else
            {
                iteration.Parent = ID;
                iteration.Project = this;
                iterations.Add(iteration);
                cards.Add(iteration.ID, iteration);

                foreach (StoryCard sc in iteration.StoryCardChildren)
                {
                    if (cards.ContainsKey(sc.ID))
                        throw new ForbiddenOperationException("Conflicting keys: ID " + sc.ID + " already in use!");
                    else
                        cards.Add(sc.ID, sc);
                }
            }
        }

        public Backlog Backlog
        {
            get { return this.backlog; }
            set
            {
               if (backlog != null)
                {
                    foreach (StoryCard sc in backlog.StoryCardChildren)
                    {
                        cards.Remove(sc.ID);
                    }
                  //  cards.Remove(backlog.ID);
                    backlog.StoryCardChildren.Clear();
                }

                value.Parent = this.ID;
                if (backlog == null)
                {
                    this.backlog = new BacklogDataObject();
                    this.backlog.Update(value);
                    cards.Add(this.backlog.ID, this.backlog);
                }
                else
                {
                    //make sure the ID changes can be updated on cards collection
                    if (this.backlog.ID != value.ID)
                    {
                        cards.Remove(this.backlog.ID);
                        this.backlog.Update(value);
                        cards.Add(this.backlog.ID, this.backlog);
                    }
                    else
                    {
                        this.backlog.Update(value);
                    }
                }
               

                foreach (StoryCard sc in value.StoryCardChildren)
                {
                    StoryCard newCard = (StoryCard)sc.Clone();
                    cards.Add(sc.ID, newCard);
                    backlog.StoryCardChildren.Add(newCard);
                }
            }
        }

        public Legend Legend
        {
            get { return this.legend; }
            set { this.legend = value; }
        }

        public List<Iteration> IterationChildren
        {
            get { return this.iterations; }
            set
            {
                foreach (Iteration iter in IterationChildren)
                {
                    RemoveIteration(iter);
                }

                foreach (Iteration iter in value)
                {
                    AddIteration(iter);
                }
            }
        }

        public List<TeamMember> TeamMemberChildren
        {
            get { return this.teamMember; }
            set{
                 this.teamMember.Clear();
                foreach(TeamMember member in value)
                {
                    AddTeamMember(member);
                }
            }
        }

        public void RemoveIteration(Iteration iteration)
        {
            iterations.Remove(iteration);

            foreach (StoryCard sc in iteration.StoryCardChildren)
            {
                cards.Remove(sc.ID);
            }

            cards.Remove(iteration.ID);
        }

        public void RemoveTeamMember(TeamMember member)
        {
            this.teamMember.Remove(member);
        }

        public Backlog CreateBacklog(double width, double height, double locationX, double locationY)
        {
            if (this.backlog != null)
            {
                throw new ForbiddenOperationException("Only one Backlog allowed per Project!", this.Backlog.ID);
            }
            else
            {
                Backlog backlog = new BacklogDataObject(width, height, locationX, locationY);
                backlog.ID = 2;
                Backlog = backlog;
                Backlog.Project = this;
                return backlog;
            }
        }

        public Backlog CreateBacklog(Backlog backlog)
        {
            if (this.backlog != null)
            {
                throw new ForbiddenOperationException("Only one Backlog allowed per Project!", this.Backlog.ID);
            }
            else
            {
                if (cards.ContainsKey(backlog.ID))
                    throw new ForbiddenOperationException("Conflicting keys: ID " + backlog.ID + " already in use!");
                else
                {
                    this.backlog = backlog;
                    cards.Add(backlog.ID, backlog);
                    return backlog;
                }
            }
        }

        public Iteration CreateIteration(string name, string description, double width, double height, double locationX, double locationY,
            float availableEffort, DateTime startDate, DateTime endDate)
        {
            Iteration iteration = new IterationDataObject(generator.GetNextID(), ID,
                name, description, width, height, locationX, locationY, (float)availableEffort, startDate, endDate, CardConstants.STATUS_DEFINED);

            this.AddIteration(iteration);

            return iteration;
        }

        public Iteration CreateIteration(Iteration iteration)
        {
            if (iteration.ID == 0)
                iteration.ID = generator.GetNextID();
            this.AddIteration(iteration);

            return iteration;
        }

        public StoryCard CreateStoryCard(string name, string description, double width, double height, double locationX, double locationY,
            long parentid, float bestCaseEstimate, float mostlikelyEstimate, float worstCaseEstimate, float actualEffort, string status, string color, string cardOwner, float rotationAngle, string rallyID, string fitID)
        {

            StoryCard storycard = new StoryCardDataObject(generator.GetNextID(), parentid,
                name, description, width, height, locationX, locationY,
                bestCaseEstimate, mostlikelyEstimate, worstCaseEstimate, actualEffort,
                status, (Color)new CardColorConverter().ConvertFrom(color), cardOwner, rotationAngle, new byte[0],rallyID,fitID);

            ((IndexCardWithChild)FindCard(parentid)).AddStoryCard(storycard);
            cards.Add(storycard.ID, storycard);

            return storycard;
        }

        public StoryCard CreateStoryCard(StoryCard storycard)
        {
            if (storycard.ID == 0)
                storycard.ID = generator.GetNextID();
            if(storycard.Parent == 0)
                storycard.Parent = Backlog.ID;
            if (cards.ContainsKey(storycard.ID))
                throw new ForbiddenOperationException("Conflicting keys: ID " + storycard.ID + " already in use!");
            else
            {
                IndexCardWithChild parent = FindCard(storycard.Parent) as IndexCardWithChild;
                parent.AddStoryCard(storycard);
                cards.Add(storycard.ID, storycard);
                return storycard;
            }
        }

        public IndexCard DeleteCard(long id)
        {
            IndexCard card = FindCard(id);

            if (card is StoryCard)
                return DeleteStoryCard((StoryCard)card);
            else if (card is Iteration)
                return DeleteIteration((Iteration)card);
            else if (card is Backlog)
                return DeleteBacklog((Backlog)card);
            else
                throw new IndexCardNotFoundException("ID does not refer to either StoryCard, Iteration or Backlog!");

        }

        private IndexCard DeleteStoryCard(StoryCard storyCard)
        {
            IndexCardWithChild parent = (IndexCardWithChild)FindCard(storyCard.Parent);

            parent.RemoveStoryCard(storyCard);
            cards.Remove(storyCard.ID);

            return storyCard;
        }

        private IndexCard DeleteBacklog(Backlog backlog)
        {
            throw new ForbiddenOperationException("You cannot delete the Backlog without deleting the Project!");
        }

        private IndexCard DeleteIteration(Iteration iteration)
        {
            Iteration iter = (Iteration)iteration.Clone();
            iterations.Remove(iteration);
            foreach (StoryCard sc in iteration.StoryCardChildren)
            {
                cards.Remove(sc.ID);
            }
    //        iteration.remove
    //        RemoveIteration(iteration);
            cards.Remove(iteration.ID);

            return iter;
        }

        public IndexCard UndeleteCard(IndexCard indexCard)
        {

            if (indexCard is Iteration)
                return UndeleteIteration((Iteration)indexCard);
            if (indexCard is StoryCard)
                return UndeleteStoryCard((StoryCard)indexCard);
            else
                throw new IndexCardNotFoundException("ID does not refer to either StoryCard, Iteration or Backlog!");

        }

        private Iteration UndeleteIteration(Iteration iteration)
        {
            AddIteration(iteration);

            return iteration;
        }

        private StoryCard UndeleteStoryCard(StoryCard storycard)
        {
            ((IndexCardWithChild)FindCard(storycard.Parent)).AddStoryCard(storycard);
            cards.Add(storycard.ID, storycard);

            return storycard;
        }

        public IndexCard FindCard(long id)
        {
            if (cards.ContainsKey(id))
                return cards[id];
            else
                throw new IndexCardNotFoundException(id);
        }

        public IndexCard UpdateCard(IndexCard indexCard)
        {
            if (indexCard is Backlog)
                return UpdateBacklog((Backlog)indexCard);
            if (indexCard is Iteration)
                return UpdateIteration((Iteration)indexCard);
            if (indexCard is StoryCard)
                return UpdateStoryCard((StoryCard)indexCard);
            else
                throw new IndexCardNotFoundException(indexCard.ID);

        }

        private Backlog UpdateBacklog(Backlog newBacklog)
        {
            Backlog.Update(newBacklog);

            return Backlog;
        }

        private Iteration UpdateIteration(Iteration newIteration)
        {
            Iteration oldIteration = (Iteration)FindCard(newIteration.ID);
            oldIteration.Update(newIteration);

            return oldIteration;
        }

        public Legend UpdateLegend(Legend legend)
        {
            this.legend = legend;
            return this.legend;
        }



        private StoryCard UpdateStoryCard(StoryCard sc)
        {
            StoryCard oldCard = (StoryCard)FindCard(sc.ID);
            oldCard.Update(sc);
            return oldCard;
        }

        public StoryCard MoveStoryCardToNewParent(long id, long oldparentid, long newparentid, double locationX, double locationY, double rotationAngle)
        {
            StoryCard storycard = (StoryCard)FindCard(id);

            if (oldparentid != newparentid)
            {
                IndexCardWithChild oldparent = (IndexCardWithChild)FindCard(oldparentid);
                IndexCardWithChild newparent = (IndexCardWithChild)FindCard(newparentid);

                if (storycard.Parent == oldparentid)
                {

                    object before = storycard.Clone();

                    oldparent.RemoveStoryCard(storycard);

                    storycard.LocationX = locationX;
                    storycard.LocationY = locationY;
                    storycard.RotationAngle = rotationAngle;

                    newparent.AddStoryCard(storycard);

                    object after = storycard.Clone();

                    //notify must be triggered here because the necessary data is only
                    //available on this call
                    storycard.Notify(StoryCardDataObject.PARENT_PROPERTY, before, after);
                }
            }

            return storycard;
        }

        #endregion

        #region INotifyPropertyChanged

        protected void Notify(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public void Notify(string propertyName, object before, object after)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new IndexCardPropertyChangedEventArgs(propertyName, before, after));
            }
        }

        #endregion

        #region Object Members

        public override string ToString()
        {
            string project = "###################################\n" + "#             Project             #\n"
                    + "###################################\n\n" + "Name:\t\t" + Name + "\n" + "ID:\t\t" + ID + "\n" + "Iterations:\t" + IterationChildren.Count + "\n\n";
            return project;
        }

        public override bool Equals(object obj)
        {
            if(obj is ProjectDataObject)
            {
                Project prj = (ProjectDataObject)obj;

                bool iterChildrenEqual = true;

                if (IterationChildren.Count == prj.IterationChildren.Count)
                {
                    for (int i = 0; i < IterationChildren.Count; i++)
                    {
                        iterChildrenEqual = iterChildrenEqual && IterationChildren[i].Equals(prj.IterationChildren[i]);
                    }
                }
                else
                {
                    return false;
                }

                bool le = prj.Legend.Equals(Legend);
                bool be = prj.Backlog.Equals(Backlog);

                return (prj.ID == ID &&
                    prj.Name.Equals(Name) &&
                    le &&
                    be &&
                    iterChildrenEqual);
            }
                return false;
        }

        public override int GetHashCode()
        {
            return (int)id;
        }

        #endregion

        #region IXmlSerializable Members

        public XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(XmlReader reader)
        {
            //if (reader.LocalName.Equals("Project"))
            //{
                Name = reader.GetAttribute("Name");
                ID = long.Parse(reader.GetAttribute("ID"));

                if (ID > maxid)
                    maxid = ID;
            

            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:

                       if (reader.LocalName.Equals("Iteration"))
                        {
                            Iteration iter = new IterationDataObject();
                            iter.ReadXml(reader);
                            AddIteration(iter);

                        }
                        else if (reader.LocalName.Equals("Backlog"))
                        {
                            Backlog back = new BacklogDataObject();
                            back.ReadXml(reader);
                            Backlog = back;
                         
                        }
                        else if (reader.LocalName.Equals("Legend"))
                        {
                            Legend lgnd = new LegendDataObject();
                            lgnd.ReadXml(reader);
                            Legend = lgnd;
                        }
/*                        else if (reader.LocalName.Equals("Project"))
                        {
                            Name = reader.GetAttribute("Name");
                            ID = long.Parse(reader.GetAttribute("ID"));

                        }*/
                        else if (reader.LocalName.Equals("USERS"))
                        {
                            TeamMember member = new TeamMemberDataObject(reader.GetAttribute("Name"));
                            AddTeamMember(member);
                        }
                        break;
                }
            }

            generator.Init(getMaxid() + 1);
        }

        private long getMaxid()
        {
            foreach (long sid in cards.Keys)
            {
                if (maxid < sid)
                    maxid = sid;
            }
            return maxid;
        }

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteStartElement("Project");
            writer.WriteAttributeString("ID", ID.ToString());
            writer.WriteAttributeString("Name", Name);

            if (IterationChildren.Count > 0)
            {
                foreach (Iteration iter in IterationChildren)
                {
                    iter.WriteXml(writer);
                }
            }

            Backlog.WriteXml(writer);
            Legend.WriteXml(writer);
            if (TeamMemberChildren.Count > 0)
            {
                foreach (TeamMember member in TeamMemberChildren)
                {
                    member.WriteXml(writer);
                }
            }
            writer.WriteEndElement();
        }

        #endregion

        #region ICloneable Members

        public object Clone()
        {
            return new ProjectDataObject(this);
        }

        #endregion

        #region Helper

        protected void RebuildIndex()
        {
            cards.Clear();

            if (Backlog != null)
            {
                cards.Add(Backlog.ID, Backlog);

                if (Backlog.StoryCardChildren != null)
                {
                    foreach (StoryCard sc in Backlog.StoryCardChildren)
                    {
                        cards.Add(sc.ID, sc);
                    }

                }
            }

            if (IterationChildren != null)
            {
                foreach (Iteration iter in IterationChildren)
                {
                    cards.Add(iter.ID, iter);

                    if (iter.StoryCardChildren != null)
                    {
                        foreach (StoryCard sc in iter.StoryCardChildren)
                        {
                            cards.Add(sc.ID, sc);
                        }
                    }
                }
            }
        }

        #endregion

        public FileSystemIDGenerator getGenerator()
        {
            return this.generator;
        }


    }
}